use anyhow::Result;
use tonic::transport::Channel;
use crate::proto::{
    transportation_service_client::TransportationServiceClient,
    *,
};
use chrono;

#[derive(Clone)]
pub struct TmsClient {
    client: TransportationServiceClient<Channel>,
}

impl TmsClient {
    pub async fn new(server_url: &str) -> Result<Self> {
        let channel = Channel::from_shared(server_url.to_string())?
            .connect()
            .await?;
        
        let client = TransportationServiceClient::new(channel);
        
        Ok(Self { client })
    }
    
    pub async fn list_vehicles(&mut self, page: i32, page_size: i32, status: i32) -> Result<ListVehiclesResponse> {
        let request = tonic::Request::new(ListVehiclesRequest {
            page,
            page_size,
            status,
        });
        
        let response = self.client.list_vehicles(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn create_vehicle(
        &mut self,
        license_plate: String,
        model: String,
        max_weight: f64,
        max_volume: f64,
        vehicle_type: i32,
        driver_id: String,
    ) -> Result<Vehicle> {
        let request = tonic::Request::new(CreateVehicleRequest {
            license_plate,
            model,
            max_weight,
            max_volume,
            r#type: vehicle_type,
            driver_id,
        });
        
        let response = self.client.create_vehicle(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn get_vehicle(&mut self, id: String) -> Result<Vehicle> {
        let request = tonic::Request::new(GetVehicleRequest { id });
        
        let response = self.client.get_vehicle(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn list_orders(&mut self, page: i32, page_size: i32, status: i32) -> Result<ListOrdersResponse> {
        let request = tonic::Request::new(ListOrdersRequest {
            page,
            page_size,
            status,
        });
        
        let response = self.client.list_orders(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn create_order(
        &mut self,
        customer_id: String,
        pickup_address: String,
        delivery_address: String,
        weight: f64,
        volume: f64,
        pickup_time_window_start: i64,
        pickup_time_window_end: i64,
        delivery_time_window_start: i64,
        delivery_time_window_end: i64,
        special_requirements: String,
    ) -> Result<Order> {
        let request = tonic::Request::new(CreateOrderRequest {
            customer_id,
            pickup_address,
            delivery_address,
            weight,
            volume,
            pickup_time_window_start,
            pickup_time_window_end,
            delivery_time_window_start,
            delivery_time_window_end,
            special_requirements,
        });
        
        let response = self.client.create_order(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn get_order(&mut self, id: String) -> Result<Order> {
        let request = tonic::Request::new(GetOrderRequest { id });
        
        let response = self.client.get_order(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn create_transport_plan(
        &mut self,
        name: String,
        description: String,
        order_ids: Vec<String>,
        vehicle_id: String,
        driver_id: String,
        planned_start_time: i64,
        planned_end_time: i64,
    ) -> Result<TransportPlan> {
        let request = tonic::Request::new(CreateTransportPlanRequest {
            name,
            description,
            order_ids,
            vehicle_id,
            driver_id,
            planned_start_time,
            planned_end_time,
        });
        
        let response = self.client.create_transport_plan(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn update_vehicle_location(
        &mut self,
        vehicle_id: String,
        latitude: f64,
        longitude: f64,
        speed: f64,
        heading: f64,
    ) -> Result<UpdateLocationResponse> {
        let request = tonic::Request::new(UpdateLocationRequest {
            vehicle_id,
            latitude,
            longitude,
            speed,
            heading,
        });
        
        let response = self.client.update_location(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn get_vehicle_location(&mut self, vehicle_id: String) -> Result<VehicleLocation> {
        let request = tonic::Request::new(GetVehicleLocationRequest { vehicle_id });
        
        let response = self.client.get_vehicle_location(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn optimize_route(&mut self, order_ids: Vec<String>, vehicle_id: String, objective: i32) -> Result<OptimizeRouteResponse> {
        let request = tonic::Request::new(OptimizeRouteRequest {
            order_ids,
            vehicle_id,
            objective,
        });
        
        let response = self.client.optimize_route(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn list_transport_plans(&mut self, page: i32, page_size: i32, status: i32) -> Result<ListTransportPlansResponse> {
        let request = tonic::Request::new(ListTransportPlansRequest {
            page,
            page_size,
            status,
        });
        
        let response = self.client.list_transport_plans(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn get_transport_plan(&mut self, id: String) -> Result<TransportPlan> {
        let request = tonic::Request::new(GetTransportPlanRequest { id });
        
        let response = self.client.get_transport_plan(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn update_order_status(&mut self, id: String, status: i32, _notes: String) -> Result<Order> {
        let order_status = match status {
            0 => OrderStatus::OrderPending,
            1 => OrderStatus::OrderAssigned,
            2 => OrderStatus::OrderPickedUp,
            3 => OrderStatus::OrderInTransit,
            4 => OrderStatus::OrderDelivered,
            5 => OrderStatus::OrderCancelled,
            _ => OrderStatus::OrderPending,
        };
        
        let request = tonic::Request::new(UpdateOrderStatusRequest {
            id,
            status: order_status as i32,
        });
        
        let response = self.client.update_order_status(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn update_vehicle_status(&mut self, id: String, status: i32, _notes: String) -> Result<Vehicle> {
        let vehicle_status = match status {
            0 => VehicleStatus::Available,
            1 => VehicleStatus::InUse,
            2 => VehicleStatus::Maintenance,
            3 => VehicleStatus::OutOfService,
            _ => VehicleStatus::Available,
        };
        
        let request = tonic::Request::new(UpdateVehicleRequest {
            id,
            license_plate: None,
            model: None,
            max_weight: None,
            max_volume: None,
            r#type: None,
            status: Some(vehicle_status as i32),
            driver_id: None,
        });
        
        let response = self.client.update_vehicle(request).await?;
        Ok(response.into_inner())
    }
    
    pub async fn get_system_stats(&mut self) -> Result<serde_json::Value> {
        // Since system stats is not defined in proto, return mock data
        Ok(serde_json::json!({
            "total_vehicles": 0,
            "active_vehicles": 0,
            "total_orders": 0,
            "pending_orders": 0,
            "in_transit_orders": 0,
            "completed_orders": 0,
            "total_transport_plans": 0,
            "active_transport_plans": 0,
            "system_uptime": 0,
            "last_updated": chrono::Utc::now().timestamp()
        }))
    }
    
    pub async fn health_check(&mut self) -> Result<serde_json::Value> {
        // Since health check is not defined in proto, return mock data
        Ok(serde_json::json!({
            "status": "healthy",
            "database_connected": true,
            "services_running": true,
            "last_check": chrono::Utc::now().timestamp(),
            "version": "1.0.0",
            "uptime": 0
        }))
    }
}